{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a89bafc4",
   "metadata": {},
   "source": [
    "# DAY28"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "226f0286",
   "metadata": {},
   "source": [
    "之前大家已经接触过类的概念，我们也反复强调了类的实例化。\n",
    "\n",
    "为了避免有的同学之前没看过复试班的内容或者已经遗忘，我们来简单的复习下\n",
    "\n",
    "类是对属性和方法的封装，可以理解为模板，通过对模板实例化可以实现调用这个类的属性和方法。比如创建一个随机森林类，然后就可以调用他的训练和预测方法。\n",
    "\n",
    "现在我们来学一下自己定义一个类，这会让我们对于类这个对象理解的更加深刻\n",
    "\n",
    "ps：类的操作很多，我们这里只说一些在深度学习领域最常见和通用的"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "73b7b3c9",
   "metadata": {},
   "source": [
    "一个常见的类的定义包括了：\n",
    "1. 关键字class\n",
    "2. 类名\n",
    "3. 语法固定符号冒号(:)\n",
    "4. 一个初始化函数__init__(self)  \n",
    "\n",
    "注意：注意：init左右各有两个下划线__，需要传入self这个特殊的参数。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3078f999",
   "metadata": {},
   "source": [
    "## Pass占位符和缩进"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "8beceba8",
   "metadata": {},
   "outputs": [],
   "source": [
    "class ClassName: # 类名通常遵循大驼峰命名法 (UpperCamelCase)，即每个单词的首字母都大写，class是定义类的关键词\n",
    "    # 类的代码块 (可以包含属性定义、方法定义等)\n",
    "    pass # pass 是一个占位符，表示这里暂时没有任何内容"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "812b4e49",
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "unexpected EOF while parsing (1085545923.py, line 3)",
     "output_type": "error",
     "traceback": [
      "\u001b[1;36m  Cell \u001b[1;32mIn[4], line 3\u001b[1;36m\u001b[0m\n\u001b[1;33m    # pass # pass 是一个占位符，表示这里暂时没有任何内容\u001b[0m\n\u001b[1;37m                                     ^\u001b[0m\n\u001b[1;31mSyntaxError\u001b[0m\u001b[1;31m:\u001b[0m unexpected EOF while parsing\n"
     ]
    }
   ],
   "source": [
    "class ClassName: # 类名通常遵循大驼峰命名法 (UpperCamelCase)，即每个单词的首字母都大写，\n",
    "    # # 类的代码块 (可以包含属性定义、方法定义等)\n",
    "    # pass # pass 是一个占位符，表示这里暂时没有任何内容"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f91742f",
   "metadata": {},
   "source": [
    "许多时候，当规划好准备写一个函数、或者一个类，关键词定义后，会先用pass占位，避免运行错误，等到想好写什么再去补上\n",
    "\n",
    "比如def、class这些定义的关键词后，必须有一个有占据缩进位置的代码块。\n",
    "\n",
    "还有下面这些依赖缩进的语句，都可以用pass语句来占位"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e2db1da2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 条件语句\n",
    "x = 10\n",
    "if x > 5:\n",
    "    pass# 如果这里是空的，就会报错，可以注释试一下，即使x=1也会报错\n",
    "else:\n",
    "    print(\"x is not greater than 5\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "5bd85a45",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 循环语句\n",
    "for i in range(3):\n",
    "    pass # 循环体是空的\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "a5b11fb0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hh\n"
     ]
    }
   ],
   "source": [
    "try:\n",
    "    # 尝试执行的代码\n",
    "    print(\"hh\")\n",
    "except SomeError:\n",
    "    pass# 捕获到异常后，这里需要代码\n",
    "finally:\n",
    "    pass# 无论如何都会执行的代码块，也需要内容"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8617a856",
   "metadata": {},
   "source": [
    "总结：\n",
    "Python 通过缩进来定义代码块的结构。当解释器遇到像 def, class, if, for 这样的语句，并且后面跟着冒号 : 时，它就期望接下来会有一个或多个缩进的语句来构成这个代码块。如果它没有找到任何缩进的语句（即代码块是空的），它就无法确定这个结构的范围，因此会抛出 IndentationError。\n",
    "\n",
    "pass 语句的存在就是为了解决这个问题：它本身不执行任何操作，但它是一个有效的 Python 语句。所以，当你需要一个语法上存在的代码块，但又暂时不想在其中放入任何实际的逻辑时，pass 就是一个完美的占位符，它告诉解释器：“这里有一个代码块，但它什么也不做。”\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c3aeebc5",
   "metadata": {},
   "source": [
    "## 类的初始化方法\n",
    "\n",
    "初始化方法又叫构造方法、特殊方法\n",
    "\n",
    "类有2种方法\n",
    "1. 初始化方法，\n",
    "2. 普通放大"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6f239322",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Susan\n"
     ]
    }
   ],
   "source": [
    "class Teacher: # 这里不需要括号\n",
    "  def __init__(self): #初始化方法，这里没有传入参数\n",
    "    self.name = \"Susan\" # 给类定义一些属性\n",
    "    self.subject = \"English\"\n",
    "    self.age = 33\n",
    "\n",
    "Teacher = Teacher() # 创建一个Teacher类的实例\n",
    "print(Teacher.name) # 输出: Susan"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cdbbb23a",
   "metadata": {},
   "outputs": [],
   "source": [
    "class Teacher:\n",
    "    def __init__(self, name, age):# 初始化方法，传入了参数\n",
    "        self.name = name # 外界的参数，需要通过self.xxx来复制给类自己的属性\n",
    "        self.age = age\n",
    "        self.subject = \"English\"  # 这个属性仍然是在创建时就设定好的\n",
    "\n",
    "# 创建一个Teacher对象的例子，构造方法的参数必须\n",
    "teacher = Teacher(\"Susan\", 33) # 如果在初始化方法中设置了非默认的参数，那么外界就必须要传入才行\n",
    "print(teacher.name)  # 输出: Susan\n",
    "print(teacher.age)   # 输出: 33\n",
    "print(teacher.subject)  # 输出: English\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0870ce03",
   "metadata": {},
   "source": [
    "其中，self.xx是用来表明这个属性“归属于”这个类自己的（self）。比如self.name，就代表着：“自己的名字”，self等于“自己”，这个self指向类的实例化地址，传入的self.xx是它的属性。\n",
    "以后要是用它的属性值，即使是从外界参数传入的，前面也必须加上self.xx，否则传入的参数没价值（例外的情况我们不提）\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1ce16086",
   "metadata": {},
   "source": [
    "## 类的普通方法\n",
    "\n",
    "除了init方法（初始化方法，又名构造方法），还包含一些普通方法（自己定义）\n",
    "\n",
    "普通方法和init方法的差别在于，init方法是类的构造方法，当创建对象时，会自动调用init方法----只要你创建这个类对象了，这个init函数就会执行\n",
    "\n",
    "普通方法是只有你调用类的这个方法的时候，函数才会执行\n",
    "\n",
    "\n",
    "\n",
    "| 特性 | __init__ 方法 | 普通方法 |\n",
    "| --- | --- | --- |\n",
    "| 调用时机 | 创建实例时自动调用 | 手动通过实例调用 |\n",
    "| 是否需要显式调用 | 否 | 是 |\n",
    "| 默认名称 | 必须是 __init__ | 自定义 |\n",
    "| 主要用途 | 初始化实例属性 | 实现类的行为逻辑 |\n",
    "| 参数要求 | 第一个参数必须是 self | 第一个参数必须是 self |\n",
    "| 返回值 | 必须返回 None（隐式） | 可以返回任意类型的值 | "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "2fcc5818",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "上课中\n",
      "批评人\n",
      "Susan\n"
     ]
    }
   ],
   "source": [
    "class Teacher:\n",
    "    def __init__(self):\n",
    "        self.name = \"Susan\"\n",
    "        self.subject = \"English\"\n",
    "        self.age = 33\n",
    "    def teach_lesson(self):\n",
    "        print(\"上课中\")\n",
    "    def criticize(self):\n",
    "        print(\"批评人\") \n",
    "t = Teacher()\n",
    "t.teach_lesson() # 调用类的方法\n",
    "t.criticize()\n",
    "print(t.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "a302f158",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Susan正在教English。\n",
      "Susan正在批评John。\n"
     ]
    }
   ],
   "source": [
    "class Teacher:\n",
    "    # 初始化方法接受参数以动态设置教师的属性\n",
    "    def __init__(self, name, subject, age):\n",
    "        self.name = name\n",
    "        self.subject = subject\n",
    "        self.age = age\n",
    "\n",
    "    # 不是init的都叫做普通方法\n",
    "    # 普通方法，模拟教师上课的行为\n",
    "    def teach_lesson(self):\n",
    "        print(f\"{self.name}正在教{self.subject}。\")\n",
    "\n",
    "    # 另一个普通方法，模拟教师批评学生的行为\n",
    "    def criticize(self, student_name):\n",
    "        print(f\"{self.name}正在批评{student_name}。\")\n",
    "\n",
    "\n",
    "# 创建Teacher类的实例\n",
    "teacher = Teacher(\"Susan\", \"English\", 33)\n",
    "\n",
    "# 调用教师的方法\n",
    "teacher.teach_lesson()\n",
    "teacher.criticize(\"John\")#普通方法的参可以等到调用该方法的时候再传"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "16306e97",
   "metadata": {},
   "source": [
    "## 类的继承\n",
    "\n",
    "类已经是比较优秀的封装了，封装了函数、封装了属性\n",
    "\n",
    "正如装饰器进一步封装了函数的可复用的功能，装饰器函数封装了函数\n",
    "\n",
    "那么有没有东西可以进一步封装类呢？这就引出了类的继承\n",
    "\n",
    "在面向对象编程中，继承允许一个类（子类）继承另一个类（父类）的属性和方法，从而实现代码复用和功能扩展。子类可以：\n",
    "\n",
    "1. 复用父类的代码（无需重新实现）。\n",
    "\n",
    "2. 重写父类的方法（修改或增强功能）。\n",
    "\n",
    "3. 添加新的方法和属性（扩展功能）。\n",
    "\n",
    "仔细观察下方实例代码的注释，写的非常详细\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dc5a4c8b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "王教授（特级教师）正在用高级方法教授数学。\n",
      "王教授正在批评李同学。\n",
      "王教授正在举办关于微积分的讲座。\n"
     ]
    }
   ],
   "source": [
    "# 先沿用之前定义的teacher类\n",
    "class Teacher:\n",
    "    def __init__(self, name, subject, age):\n",
    "        self.name = name\n",
    "        self.subject = subject\n",
    "        self.age = age\n",
    "\n",
    "    def teach_lesson(self):\n",
    "        print(f\"{self.name}正在教{self.subject}。\")\n",
    "\n",
    "    def criticize(self, student_name):\n",
    "        print(f\"{self.name}正在批评{student_name}。\")\n",
    "\n",
    "# 继承 Teacher 类，起名特级教师\n",
    "class MasterTeacher(Teacher): # 1. 继承需要在括号中指定父类\n",
    "    def __init__(self, name, subject, age, experience_years):# 2. 继承的时候需要调用父类的构造方法，所以需要传入父类的参数，同时也可以传入自己的参数\n",
    "        # 调用父类的构造方法初始化基本属性\n",
    "        super().__init__(name, subject, age) # 3. 调用父类的构造方法，这里的super()是一个内置函数，返回父类的实例\n",
    "        # 4. 此时子类自动拥有了父类的属性和方法\n",
    "        # 添加子类特有的属性\n",
    "\n",
    "        self.experience_years = experience_years # 5. 子类特有的属性可以在这里定义\n",
    "\n",
    "    # 重写父类方法，增强功能-----如果子类定义了与父类同名的方法，子类实例会优先调用子类的方法。\n",
    "    def teach_lesson(self): # 6. 重写父类的方法\n",
    "        print(f\"{self.name}（特级教师）正在用高级方法教授{self.subject}。\")\n",
    "\n",
    "    # 新增子类特有的方法\n",
    "    def give_lecture(self, topic): \n",
    "        print(f\"{self.name}正在举办关于{topic}的讲座。\")\n",
    "\n",
    "# 创建子类实例\n",
    "master = MasterTeacher(\"王教授\", \"数学\", 45, 20)\n",
    "\n",
    "# 调用继承的方法\n",
    "master.teach_lesson()     # 调用重写的父类的方法\n",
    "master.criticize(\"李同学\")  # 调用父类的方法，如果不修改方法，则可以直接继承父类的方法\n",
    "\n",
    "# 调用子类特有的方法\n",
    "master.give_lecture(\"微积分\")  # 调用子类新增的方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "e5de9f95",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "旺财 发出声音\n",
      "汪汪叫\n"
     ]
    }
   ],
   "source": [
    "# super()函数 除了在构造方法中使用，还可以在其他方法中使用\n",
    "\n",
    "# 定义一个父类\n",
    "class Animal:\n",
    "    def __init__(self, name, age):\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "\n",
    "    def speak(self):\n",
    "        print(f\"{self.name} 发出声音\")\n",
    "    \n",
    "class Dog(Animal):\n",
    "    def speak(self):\n",
    "        super().speak()  # 先调用父类的方法\n",
    "        print(\"汪汪叫\")    # 再添加子类的行为\n",
    "\n",
    "dog = Dog(\"旺财\", 3)\n",
    "dog.speak() "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e8c6a1f",
   "metadata": {},
   "source": [
    "所以重写父类的方法，也包含2种\n",
    "1. 直接重写：本质是会优先用子类的同名方法，完全替代父类方法，父类逻辑不会执行。\n",
    "2. 使用super()重写，保留父类方法的核心逻辑，并在其基础上扩展。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "vs",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
